package yhao.demo.autocode.core.sqlmapper;


import org.apache.commons.lang.StringUtils;
import yhao.demo.autocode.core.AutoCodeUtils;
import yhao.demo.autocode.core.sqlmapper.connect.*;
import yhao.infra.common.util.CommonMapUtil;
import yhao.infra.common.util.CommonStringUtil;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * Created by yoara on 2016/7/12.
 */
public class SqlModuleMapHelper {
    //数据库连接
    private DbConnectParam dbParam;
    //包名
    private String packageName;
    //包名
    private String webPackageName;
    //数据库名
    private String dbName;
    //表名
    private String tableName;
    //字段前缀
    private String columnPrefix;
    //对象名，以此生成对象、service、dao、xml的通用名
    private String objectName;
    private String objectNameSmall;
    private String objectNameBig;
    //通用简单描述前缀
    private String desc;
    private String author;
    //_下划线驼峰处理
    private boolean underLineToCamelCase = true;
    //xml的model使用类路径
    private boolean xmlModelWithPackagePath = true;

    //生成目录
    private String rootDir;

    public final static String CONF_DIR_PATH =
            System.getProperty("user.home") + File.separator + "yhao.sqlToModule";

    /**
     * @param dbParam      数据库连接
     * @param dbName       数据库名
     * @param tableName    表名
     * @param columnPrefix 表字段前缀，如fk，FK等
     * @param packageName  包名
     * @param webPackageName  web层包名
     * @param objectName   Model类名
     * @param desc         描述前缀
     * @param author       作者
     */
    public SqlModuleMapHelper(DbConnectParam dbParam, String dbName, String tableName,
                              String columnPrefix, String packageName, String webPackageName,
                              String objectName, String desc, String author) {
        this.dbParam = dbParam;
        this.dbName = dbName;
        this.tableName = tableName;
        this.columnPrefix = columnPrefix;
        this.packageName = packageName;
        this.webPackageName = webPackageName;
        this.objectName = objectName;
        this.desc = desc;
        this.author = author;
        objectNameSmall = (objectName.charAt(0) + "").toLowerCase() + objectName.substring(1);
        objectNameBig = CommonStringUtil.toUpperCase(objectName,true);
    }

    public void doMapper() throws Exception {
        System.out.println(toString());
        SchemeInfo scheme = DbConnectionUtil.getAllSchemeInfo(dbParam,dbName,tableName);
        System.out.println("开始生成...");
        //生成根目录
        makeRoot();
        //生成对象文件
        makeObject(scheme);
        //生成模板文件
        makeTemplate();
        //生成Dao.xml
        makeMyBatisXml(scheme);
        System.out.println("完成操作...");
        System.out.println("请到以下地址复制文件..." + CONF_DIR_PATH);
    }

    private void makeRoot() {
        File root = new File(CONF_DIR_PATH);
        if (!root.exists()) {
            root.mkdir();
        }
        String fileDirPath = CONF_DIR_PATH + File.separator + objectNameSmall;
        File fileDir = new File(fileDirPath);
        if (!fileDir.exists()) {
            fileDir.mkdir();
        }
        rootDir = fileDirPath;
    }


    //生成POJO对象文件，包括model和insertForm,editForm
    private void makeObject(SchemeInfo scheme) throws IOException {
        List<SqlModelDef> defs = scheme.getDefList();
        StringBuffer modelAll = new StringBuffer();
        modelAll.append(String.format("package %s.apilist.model;\n\n", packageName));
        StringBuffer formAll = new StringBuffer();
        formAll.append(String.format("package %s.apilist.form;\n\n", packageName));

        StringBuffer modelImport = new StringBuffer();
        StringBuffer formImport = new StringBuffer();
        StringBuffer modelFieldSb = new StringBuffer();
        StringBuffer formFieldSb = new StringBuffer();
        StringBuffer getSetSb = new StringBuffer();
        for (int i = 1; i < defs.size(); i++) {
            SqlModelDef dbField = defs.get(i);
            String objectField = getObjectString(dbField);
            String fieldType = getType(dbField.getDataType(),dbParam.getDbType());
            if (fieldType.equals("BigDecimal")) {
                modelImport.append("import java.math.BigDecimal;\n");
                formImport.append("import java.math.BigDecimal;\n");
            } else if (fieldType.equals("Integer") || fieldType.equals("Long")) {
                modelImport.append("import java.lang.*;\n");
                formImport.append("import java.lang.*;\n");
            } else if (fieldType.equals("Date")){
                modelImport.append("import java.util.Date;\n");
                formImport.append("import java.util.Date;\n");
            }
            //field
            modelFieldSb.append(String.format("\t@ApiModelProperty(value = \"%s\")\n", dbField.getColumnComment()));
            modelFieldSb.append(String.format("\tprivate %s %s;\n\n", fieldType, objectField));
            formFieldSb.append(String.format("\t@ApiModelProperty(value = \"%s\"%s)\n",
                    dbField.getColumnComment(),dbField.isCanNull()?"":",required = true"));
            if(!dbField.isCanNull()){
                formFieldSb.append(String.format("\t%s(message = \"请输入%s\")\n",
                        fieldType.equals("String")?"@NotBlank":"@NotNull",dbField.getColumnComment()));
                if(fieldType.equals("String")){
                    formImport.append("import org.hibernate.validator.constraints.NotBlank;\n");
                }else{
                    formImport.append("import javax.validation.constraints.NotNull;\n");
                }
            }
            formFieldSb.append(String.format("\tprivate %s %s;\n\n", fieldType, objectField));

            //getSet方法
            String _objectField = (objectField.charAt(0) + "").toUpperCase() + objectField.substring(1);

            getSetSb.append(String.format("\tpublic void set%s(%s %s){\n", _objectField, fieldType, objectField));
            getSetSb.append(String.format("\t\tthis.%s = %s;\n\t}\n\n", objectField, objectField));
            getSetSb.append(String.format("\tpublic %s get%s(){\n", fieldType, _objectField));
            getSetSb.append(String.format("\t\treturn %s;\n\t}\n\n", objectField));
        }

        modelImport.append("import yhao.infra.common.model.Entity;\n");
        formImport.append("import yhao.infra.apilist.validate.IdUnForm;\n");
        modelImport.append("import io.swagger.annotations.ApiModel;\n");
        formImport.append("import io.swagger.annotations.ApiModel;\n");
        modelImport.append("import io.swagger.annotations.ApiModelProperty;\n");
        formImport.append("import io.swagger.annotations.ApiModelProperty;\n");
        modelImport.append(getClassDesc("Model"));
        formImport.append(getClassDesc("保存Form"));
        //String apiModel = String.format("@ApiModel(value = \"%s\"%s)\n",desc,
        //        StringUtils.isNotBlank(scheme.getTableDesc())?",description = \""+scheme.getTableDesc()+"\"":"");
        String apiModel = String.format("@ApiModel(description = \"%s\")\n",scheme.getTableDesc());
        modelImport.append(apiModel);

        modelImport.append(String.format("public class %sModel extends Entity<String> {\n",objectName));
        formImport.append(String.format("public class %sInsertForm extends IdUnForm {\n",objectName));

        modelAll.append(modelImport);
        modelAll.append(modelFieldSb).append("\n");
        modelAll.append(getSetSb);
        modelAll.append("}\n");

        formAll.append(formImport);
        formAll.append(formFieldSb).append("\n");
        formAll.append(getSetSb);
        formAll.append("}\n");
        AutoCodeUtils.makeFile(rootDir,objectName+"Model.java",modelAll.toString());
        String formAllStr = formAll.toString();
        AutoCodeUtils.makeFile(rootDir,objectName+"InsertForm.java",formAllStr);
        formAllStr = formAllStr.replaceAll("InsertForm","EditForm")
                .replaceAll("IdUnForm","IdForm");
        AutoCodeUtils.makeFile(rootDir,objectName+"EditForm.java",formAllStr);
    }

    private String getObjectString(SqlModelDef dbField) {
        String objectField = dbField.getColumnName().toLowerCase();
        if (!StringUtils.isEmpty(columnPrefix)) {
            objectField = objectField.substring(columnPrefix.length());
        }
        objectField = (objectField.charAt(0) + "").toLowerCase() + objectField.substring(1);
        if (underLineToCamelCase) {
            String[] underLineWords = objectField.split("_");
            if (underLineWords.length > 1) {
                StringBuffer sbuffer = new StringBuffer();
                sbuffer.append(underLineWords[0]);
                for (int wordIndex = 1; wordIndex < underLineWords.length; wordIndex++) {
                    sbuffer.append((underLineWords[wordIndex].charAt(0) + "").toUpperCase() +
                            underLineWords[wordIndex].substring(1));
                }
                objectField = sbuffer.toString();
            }
        }
        return objectField;
    }


    private static final String DATE_F = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    private String getClassDesc(String descMore) {
        return String.format("/**\n * %s\n * Created by %s on %s\n */\n",
                desc + descMore, author,
                DATE_F, objectName);
    }

    //由模板生成文件
    private void makeTemplate() throws IOException {
        Map<String,String> param = CommonMapUtil.NEW();
        param.put("rootPackage",packageName);
        param.put("webRootPackage",webPackageName);
        param.put("objectName",objectName);
        param.put("objectNameSmall",objectNameSmall);
        param.put("objectNameBig",objectNameBig);
        param.put("date",DATE_F);
        param.put("author",author);
        param.put("desc",desc);

        String content = AutoCodeUtils.readResource("sqlToModel/api.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"Api.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/controller.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"ManagerController.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/dao.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"Dao.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/feign.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"Feign.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/queryForm.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"QueryForm.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/remote.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"ManagerRemote.java",content);
        content = AutoCodeUtils.readResource("sqlToModel/service.new");
        content = AutoCodeUtils.replacePlaceHolder(content,param);
        AutoCodeUtils.makeFile(rootDir,objectName+"Service.java",content);
    }

    //生成xml
    private void makeMyBatisXml(SchemeInfo scheme) throws IOException {
        List<SqlModelDef> defs = scheme.getDefList();
        String modelPackagePath = String.format("%s.apilist.model.", packageName);
        String modelName = (xmlModelWithPackagePath?modelPackagePath:"") + objectName +"Model";
        StringBuffer all = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \n" +
                "\"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n\n");
        StringBuffer sbMap = new StringBuffer(String.format("<mapper namespace=\"%s.dao.automapper.%sDao\">\n",
                packageName, objectName));
        String pKeyColumn = "FID";
        for (int i = 0; i < defs.size(); i++) {
            SqlModelDef dbField = defs.get(i);
            if (dbField.isPrimaryKey()) {
                pKeyColumn = dbField.getColumnName();
                break;
            }
        }
        sbMap.append(String.format("\t<resultMap id=\"%sModelResult\" type=\"%s\">\n", objectName, modelName));

        String selectById = String.format("\t<select id=\"selectById\" parameterType=\"string\" " +
                "resultMap=\"%sModelResult\">\n" +
                "\t\tselect *\n" +
                "\t\tfrom %s.%s\n" +
                "\t\twhere %s=#{id}\n" +
                "\t</select>\n\n",objectName,dbName,tableName,pKeyColumn);

        String delete = String.format("\t<delete id=\"deleteById\" parameterType=\"string\">\n" +
                "\t\tdelete from %s.%s\n" +
                "\t\twhere %s=#{id}\n" +
                "\t</delete>\n",dbName,tableName,pKeyColumn);

        String queryPage = String.format("\t<select id=\"query%sPage\" " +
                "parameterType=\"%s.apilist.form.%sQueryForm\" " +
                "resultMap=\"%sModelResult\">\n" +
                "\t\tselect *\n" +
                "\t\tfrom %s.%s\n" +
                "\t\twhere 1=1\n" +
                "\t\t<if test=\"keyword!=null and keyword!=''\">\n" +
                "\t\t\t\n" +
                "\t\t</if>\n" +
                "\t</select>\n",objectName,packageName,objectName,objectName,dbName,tableName);

        String insertFormClass = packageName + ".apilist.form."+objectName+"InsertForm";
        String editFormClass = packageName + ".apilist.form."+objectName+"EditForm";
        StringBuffer sbInsert = new StringBuffer(
                String.format("\t<insert id=\"insert%s\" parameterType=\"%s\">\n", objectName, insertFormClass));
        sbInsert.append(String.format("\t\tinsert into %s.%s(", dbName,tableName));

        StringBuffer sbInsertValues = new StringBuffer(" values(");

        StringBuffer sbUpdate = new StringBuffer(
                String.format("\t<update id=\"update%s\" parameterType=\"%s\">\n", objectName, editFormClass));
        sbUpdate.append(String.format("\t\tupdate %s.%s\n\t\t<set>\n", dbName,tableName));

        for (int i = 0; i < defs.size(); i++) {
            SqlModelDef dbField = defs.get(i);
            String objectField = getObjectString(dbField);
            if (dbField.isPrimaryKey()) {
                sbMap.append(String.format("\t\t<id property=\"%s\" column=\"%s\"/>\n", objectField, dbField.getColumnName()));
            } else {
                sbMap.append(String.format("\t\t<result property=\"%s\" column=\"%s\"/>\n", objectField, dbField.getColumnName()));
            }
            sbInsert.append(dbField.getColumnName()).append(",");
            sbInsertValues.append("#{").append(objectField).append("},");
            if (!dbField.isPrimaryKey()) {
                sbUpdate.append(String.format("\t\t\t<if test=\"%s!=null\">%s=#{%s},</if>\n",
                        objectField, dbField.getColumnName(), objectField));
            }
        }
        sbMap.append("\t</resultMap>\n\n");

        sbInsert.setCharAt(sbInsert.length() - 1, ')');
        sbInsertValues.setCharAt(sbInsertValues.length() - 1, ')');
        sbInsert.append("\n\t\t\t").append(sbInsertValues).append("\n");
        sbInsert.append("\t</insert>\n\n");

        sbUpdate.append("\t\t</set>\n\t\twhere "+pKeyColumn+"=#{id}\n");
        sbUpdate.append("\t</update>\n\n");
        all.append(sbMap).append(selectById).append(queryPage)
                .append(sbInsert).append(sbUpdate).append(delete).append("\n</mapper>");
        AutoCodeUtils.makeFile(rootDir, objectName+"Dao.xml",all.toString());
    }

    //获取类型
    private String getType(String dataType, DbType dbType) {
        String type = dbType.getTypeMap().get(dataType.toUpperCase());
        if (type != null) {
            return type;
        }
        return "String";
    }

    @Override
    public String toString() {
        return "开始生成文件...信息如下：\n" +
                ", 数据库连接='" + dbParam + "\'\n" +
                ", 数据库名='" + dbName + "\'\n" +
                ", 表名='" + tableName + "\'\n" +
                ", 字段前缀='" + columnPrefix + "\'\n" +
                ", 下划线驼峰处理='" + underLineToCamelCase + "\'\n" +
                ", 包名='" + packageName + "\'\n" +
                ", model类名='" + objectName + "\'\n" +
                ", 通用描述='" + desc + "\'\n";
    }

    public void setUnderLineToCamelCase(boolean underLineToCamelCase) {
        this.underLineToCamelCase = underLineToCamelCase;
    }

    public void setXmlModelWithPackagePath(boolean xmlModelWithPackagePath) {
        this.xmlModelWithPackagePath = xmlModelWithPackagePath;
    }
}
